<?php

/**
 * Trida zajistuje nacitani presentru z pouzite adresarove struktury.
 *
 * @author David Dusek
 */
namespace CMS;

use Nette\Application as NA,
    Nette;

class PresenterFactory extends NA\PresenterFactory {
    /** @var string */
    private $baseDir;

    /** @var array */
    private $cache = array();
    
    private $moduleName = null;

    public function __construct($baseDir, Nette\DI\IContainer $context) {
        $this->baseDir = $baseDir;
        
        parent::__construct($baseDir, $context);
    }
    
    public function getPresenterClass(& $name) {
        if (isset($this->cache[$name])) {
            list($class, $name) = $this->cache[$name];
            return $class;
        }

        if (!is_string($name) || !Nette\Utils\Strings::match($name, "#^[a-zA-Z\x7f-\xff][a-zA-Z0-9\x7f-\xff:]*$#")) {
            throw new NA\InvalidPresenterException("Presenter name must be alphanumeric string, '$name' is invalid.");
        }

        $class = $this->formatPresenterClass($name);

        if (!class_exists($class)) {
            // internal autoloading
            $file = $this->formatPresenterFile($name);                        
            if (is_file($file) && is_readable($file)) {
                Nette\Utils\LimitedScope::load($file, TRUE);
            } else {
                $file = $this->formatModulPresenterFile($name);                        
                if (is_file($file) && is_readable($file)) {
                    Nette\Utils\LimitedScope::load($file, TRUE);
                }
            }

            if (!class_exists($class)) {                
                throw new NA\InvalidPresenterException("Cannot load presenter '$name', class '$class' was not found in '$file'.");
            }
        }

        $reflection = new Nette\Reflection\ClassType($class);
        $class = $reflection->getName();

        if (!$reflection->implementsInterface('Nette\Application\IPresenter')) {
            throw new NA\InvalidPresenterException("Cannot load presenter '$name', class '$class' is not Nette\\Application\\IPresenter implementor.");
        }

        if ($reflection->isAbstract()) {
            throw new NA\InvalidPresenterException("Cannot load presenter '$name', class '$class' is abstract.");
        }

        // canonicalize presenter name
        $realName = $this->unformatPresenterClass($class);
        if ($name !== $realName) {
            if ($this->caseSensitive) {
                throw new NA\InvalidPresenterException("Cannot load presenter '$name', case mismatch. Real name is '$realName'.");
            } else {
                $this->cache[$name] = array($class, $realName);
                $name = $realName;
            }
        } else {
            $this->cache[$name] = array($class, $realName);
        }

        return $class;
    }
    
    private function formatModulPresenterFile($presenter) {
        // presenter = User:User
        // substr 0 
        
        $module = substr($presenter, 0, strpos($presenter, ':'));
        $module = lcfirst($module);
        $presenterName = substr($presenter, strpos($presenter, ':') + 1, strlen($presenter) - 1);
        
        return  $this->baseDir . "/modules/$module/presenters/$presenterName" . "Presenter.php";
    }
    
    public function formatPresenterClass($presenter) {
        $colonPos = strpos($presenter, ':');
        if ($colonPos !== false) {
            $this->moduleName = substr($presenter, 0, $colonPos + 1);
            $colonPos++;            
        } else {
            $colonPos = 0;
            $this->moduleName = '';
        }
        
        return substr($presenter, $colonPos) . 'Presenter';
    }
    
    public function unformatPresenterClass($class) {
        $class = substr($class, 0, -9);
        
        if ($this->moduleName === null) {
            return $class;
        } else {
            return $this->moduleName . $class;
        }
    }

}
